package top.xia17.plugins.fast.common.utils;


import jakarta.validation.constraints.NotNull;

import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 树结构 工具类
 * 使用方法 ：
 * 1.0.0 实体需要实现 {@link TreeNode} 接口 ， 然后使用该工具类构造树
 * 1.1.0 实体可不实现 {@link TreeNode} 接口
 * @author xia17
 * @date 2020/11/13 18:02
 * @since 1.1.0
 */
public class TreeUtil {


    /**
     * 构建树
     * @param root 父节点
     * @param findChildrenFunc 通过父节点查询子节点的方法
     * @param getIdFunc 获取ID的方法
     * @param setChildrenConsumer 设置子节点的方法
     * @param <T> 节点
     * @param <R> 节点的ID
     */
    public static <T, R> void build(T root, Function<R, List<T>> findChildrenFunc, Function<T, R> getIdFunc,
                                    BiConsumer<T, List<T>> setChildrenConsumer) {
        List<T> childrenList = findChildrenFunc.apply(getIdFunc.apply(root));
        setChildrenConsumer.accept(root, childrenList);
        for (T t : childrenList) {
            build(t, findChildrenFunc, getIdFunc, setChildrenConsumer);
        }
    }

    /**
     * 构建树
     * @param roots 父节点
     * @param findChildrenFunc 通过父节点查询子节点的方法
     * @param getIdFunc 获取ID的方法
     * @param setChildrenConsumer 设置子节点的方法
     * @param <T> 节点
     * @param <R> 节点的ID
     */
    public static <T, R> void build(List<T> roots, Function<R, List<T>> findChildrenFunc, Function<T, R> getIdFunc,
                                    BiConsumer<T, List<T>> setChildrenConsumer) {
        for (T root : roots) {
            build(root, findChildrenFunc, getIdFunc, setChildrenConsumer);
        }
    }



    /**
     * 使用递归在源数据中查询父节点的子节点 ， 并添加到父节点的children中
     * @param roots 父节点
     * @param list 子节点
     * @param <T> /
     * @param <R>  /
     */
    public static <T extends TreeNode<T, R>, R> void findAndSetChildren(List<T> roots, List<T> list) {
        for (T root : roots) {
            root.findAndSetChildren(list);
            if (root.getChildrenNodes()!=null && root.getChildrenNodes().size() != 0){
                findAndSetChildren(root.getChildrenNodes(),list);
            }
        }
    }

    /**
     * 构建树
     * 通过从 父节点为Null 来判断
     * @param list /
     * @param <T> /
     * @param <R> /
     * @return /
     */
    public static<T extends TreeNode<T, R>,R> List<T> buildByRootParentIdIsNull(List<T> list){
        List<T> roots = list.stream().filter(T::isRootByParentIsNull).collect(Collectors.toList());
        TreeUtil.findAndSetChildren(roots,list);
        return roots;
    }

    /**
     * 构建树
     * 通过从 list中没有找到父节点来确定根节点
     * @param list /
     * @param <T> /
     * @param <R> /
     * @return /
     */
    public static<T extends TreeNode<T, R>,R> List<T> buildByRootNotFindPatent(List<T> list){
        List<T> roots = list.stream().filter(n->n.isRootByNotFindPatent(list)).collect(Collectors.toList());
        TreeUtil.findAndSetChildren(roots,list);
        return roots;
    }


    /**
     * 构建树
     * @param <T> 节点
     * @param <R> 节点主键
     * @param root 根节点
     * @param findChildren 查询子节点的方法
     */
    public static<T extends TreeNode<T, R>,R> void build(T root , Function<R,List<T>> findChildren){
        List<T> childrenList = findChildren.apply(root.getNodeId());
        root.setChildrenNodes(childrenList);
        for (T t : childrenList) {
            build(t,findChildren);
        }
    }


    /**
     * 汇集树的所有节点
     * @param rootId 跟节点
     * @param findChildren 查询子节点的方法
     * @param result 结果
     * @param <T> 节点
     * @param <R> 节点主键
     */
    public static<T extends TreeNode<T, R>,R> void collectTreeNodeId(R rootId,Function<R,List<T>> findChildren,@NotNull List<R> result){
        result.add(rootId);
        // 获取子节点
        List<T> list = findChildren.apply(rootId);
        if (list == null || list.size() == 0){
            return;
        }
        for (T t : list) {
            collectTreeNodeId(t.getNodeId(),findChildren,result);
        }
    }

    /**
     * 汇集树的所有节点
     * @param rootId 跟节点
     * @param findChildren 查询子节点的方法
     * @param result 结果
     * @param <R> 节点主键
     */
    public static<R> void collectTreeNodeIdById(R rootId,Function<R,List<R>> findChildren,@NotNull List<R> result){
        result.add(rootId);
        // 获取子节点
        List<R> list = findChildren.apply(rootId);
        if (list == null || list.size() == 0){
            return;
        }
        for (R r : list) {
            collectTreeNodeIdById(r,findChildren,result);
        }
    }

    /**
     * 汇集树的一个属性
     * @param trees 树
     * @param getChildrenFunc 获取子节点的方法
     * @param getPropertyFunc 获取需要汇集的属性方法
     * @param res 结果
     * @param <T> 树节点泛型
     * @param <R> 属性泛型
     * @return 结果
     */
    public static<T,R,C extends Collection<R>> C collectTreeProperty(List<T> trees, Function<T,List<T>> getChildrenFunc , Function<T,R> getPropertyFunc , C res){
        if (trees == null){
            return res;
        }
        for (T tree : trees) {
            res.add(getPropertyFunc.apply(tree));
            collectTreeProperty(getChildrenFunc.apply(tree),getChildrenFunc,getPropertyFunc,res);
        }
        return res;
    }





}
